///	@file Mul.cpp
#include "Mul.h"
#include "operators.h"
#include "Sym.h"
#include "Number.h"
#include "Pow.h"
#include "Utilities.h"
namespace sympycpp {
Mul::Mul(Ex * & v, Ex * & x, const allocationPolicy flag) : Ex(MUL) {
	sign(v->sign() == x->sign());
	v->sign(plus);
	x->sign(plus);
	args_.push_back(v);
	args_.push_back(x);
	x = 0;
	v = 0;
}
Mul::Mul(const Mul & o) : Ex(MUL) {
	sign(o.sign());
	for (const_iterator i = o.args_.begin(); i != o.args_.end(); ++i) {
		Ex * pl = (*i)->copy(); // allocation of new identical object 
		args_.push_back(pl);
	}
}
Mul::Mul(const Mul & o, const_iterator omiter) : Ex(MUL) {
	assert(o.args_.size() > 2);
	sign(o.sign());
	for (const_iterator i = o.args_.begin(); i != o.args_.end(); ++i) {
		if (i != omiter) {
			Ex * pl = (*i)->copy(); // allocation of new identical object 
			args_.push_back(pl);
		}
	}
}
Mul::Mul(basic_container & v, const allocationPolicy) : Ex(MUL) { //stealing constructor
	assert(v.size() > 1);
	v.swap(args_);
	oper_sort();
}
Mul::~Mul(){
	for (iterator i = args_.begin(); i != args_.end(); ++i) {
		delete (*i);
	}
}
Ex * Mul::copy() const { 
	return new Mul(*this); 
}
bool Mul::omit(iterator & index) {
	assert(index != args_.end());
	if (args_.size() == 2) {
		return false;
	}
	iterator rem(index); 
	args_.erase(index, ++rem);
	return true;
}
std::string Mul::treeView(const int i) const {
	std::ostringstream stream;
	spaces(i, stream);
	char ch = sign() ? '+' : '-';
	stream << ">("<< ch <<")MUL"<< std::endl;
	for (const_iterator j = args_.begin(); j != args_.end(); ++j) {
		stream << (*j)->treeView(i+1);
	}
	return stream.str();
}
size_t Mul::size() const {
	return args_.size();
}
std::string Mul::str() const {
	if (sign()) {
		return unsigned_str();
	}
	else {
		return "-" + unsigned_str();
	}
}
std::string Mul::unsigned_str() const {
	const_iterator first = args_.begin();
	const_iterator second = args_.begin();
	++second;
	if (isMultiple() && (*second)->isMul()) {
		return (*first)->str() + ((Mul *)(*second))->mulStr("/");
	}
	return mulStr("1/"); 
}
std::string Mul::mulStr(const std::string & pre) const {
	std::vector<const_iterator> index;
	std::string s = "";
	for (const_iterator i = args_.begin(); i != args_.end(); ++i) {
		if ((*i)->isNum() && ((Number *)(*i))->eq(1)) {
			assert(false);// multiplicity is cut by unsigned_str
			continue;
		}
		if ((*i)->isPow() && !(((Pow *)(*i))->isExponentPositive())) {
			index.push_back(i); //fraction: storing denominators
			continue;
		}
		if ((*i)->isAdd()) {
			s += "(" + (*i)->str() + ")";
		}
		else{
			s += (*i)->str();
		}
	}
	if (index.size() > 0) { // is is fraction?
		switch (args_.size() - index.size()) {
			case 0: //nominator does not exist => use default 
				s = pre;
				break;
			default://denominator exists 
				s += "/";
				break;		
		}
		std::string s1 = "";
		for (size_t i = 0; i < index.size(); ++i) {
			s1 += ((Pow *)(*index[i]))->abs_Exp_str();
		}
		if (index.size() == 1) {
			s += s1;
		}
		else {
			s += "(" + s1 + ")";
		}
	}
	return s;
}
Ex * Mul::diff(const Sym & x) const {
	Ex * diffExp = 0;
	if (this->isMultiple()) { // (number*anything)' == number*(anything')
		diffExp = (*(++args_.begin()))->diff(x);
		Ex * multiplicity = (*args_.begin())->copy();
		multiplicity->sign(this->sign());
		diffExp =  Operations::multiplication(multiplicity, diffExp, STEALING);
		return diffExp;
	}
	else {
		if (args_.size() == 2) { // only two multiplicands
			const_iterator secondM = args_.begin();
			const_iterator firstM = secondM++;
			if ((*firstM)->isPow() && !(*secondM)->isPow()) { // ((X^Y)*Z)' --> (Z*(X^Y))'
				swap(firstM, secondM);
			} 
			if (!(*firstM)->isPow() && (*secondM)->isPow()) { // (Z*(X^Y))'
				Pow * denominator = (Pow *)*secondM;
				Ex * numerator = *firstM;
				if (denominator->isExponentInteger() && !denominator->isExponentPositive()) {
					// (g/f^Number) --> (g'f-gf')/f^2
					Ex * g = numerator->copy(); // g
					Ex * dg = g->diff(x);  // g'
					Ex * f  = denominator->copyInverted();
					Ex * df = f->diff(x);  // f'
					Ex * dgf = Operations::multiplication(dg, f, STEALING);// g'f
					Ex * gdf = Operations::multiplication(g, df, STEALING);// gf'
					gdf->sign(!gdf->sign()); // - -> + or + -> -
					Ex * b = Operations::addition(dgf, gdf, STEALING); // g'f +/- gf'
					Ex * den = denominator->copy(); // 1/f
					Ex * number = new Number(2);
					Ex * f2 =  Operations::power(den, number, STEALING);// f^-2
					f2->sign(f2->sign() == this->sign());
					return Operations::multiplication(b, f2, STEALING);//
				}
			}
		 	if ( (*firstM)->isSym()) {
				if (((Sym *)(*firstM))->title() != x.title()) { //a*? dfx
					if ((*secondM)->isSym()) {
						if (((Sym *)(*secondM))->title() != x.title()) { //a*b dfx --> 0
							return new Number(0);
						}
						else {// [-|+]ax dfx --> [-|+]a
							Ex * firstConst = (*firstM)->copy();
							firstConst->sign(this->sign());
							return firstConst;
						}
					}
					else { // af(x) dfx --> af'(x)
						Ex * firstConst = (*firstM)->copy();
						firstConst->sign(this->sign());
						Ex * secondDiff = (*secondM)->diff(x);
						return Operations::multiplication(firstConst, secondDiff, STEALING);
					}
				}
				else { // x*? dfx
		 			if ( (*secondM)->isSym()) {
						if (((Sym *)(*secondM))->title() != x.title()) { //[-|+]bx -> [-|+]b
							Ex * secondConst = (*secondM)->copy();
							secondConst->sign(this->sign());
							return secondConst;
						}
					}
				}
			}
			else {
			 	if ( (*secondM)->isSym()) { // f(x)*a --> a*f'(x)
					if (((Sym *)(*secondM))->title() != x.title()) { 
						Ex * secondConst = (*secondM)->copy();
						secondConst->sign(this->sign());
						Ex * firstDiff = (*firstM)->diff(x);
						return Operations::multiplication(firstDiff, secondConst, STEALING);

					}
				}
			} // (fg)' --> f'g + fg'
			Ex * first = (*firstM)->copy(); // f
			first->sign(first->sign() == this->sign()); 
			Ex * firstDiff = first->diff(x); // f'
			Ex * second = (*secondM)->copy(); // g
			Ex * secondDiff = (*secondM)->diff(x); // g'
			first = Operations::multiplication(first, secondDiff, STEALING); // fg'
			second = Operations::multiplication(second, firstDiff, STEALING); // f'g	
		//	tmp1->sign(this->sign() == tmp1->sign());
		//	tmp2->sign(this->sign() == tmp2->sign());
			diffExp = Operations::addition(first, second, STEALING); // fg' + f'g
			return diffExp;
		}
		else { // function omit() needs more than 2 multiplicands
			// (fghi...)' --> f'ghi... + fg'hi... + fgh'i... + fghi'... + ...
			basic_container st;
			for (const_iterator i = args_.begin(); i != args_.end(); ++i) {
				Ex * mulCopy = new Mul(*this, i); // fghi... --> fhi... constructor omit element *i 
				Ex * diffSubExp = (*i)->diff(x); // g'
				Ex * tmp = Operations::multiplication(mulCopy, diffSubExp, STEALING); // fg'hi...
			//	tmp->sign(this->sign() == tmp->sign());
			//	diffExp = Operations::addition(diffExp, tmp, STEALING); // f'ghi... + fg'hi...
				st.push_back(tmp);
			}
			diffExp = Utilities::constructorAdd(st, STEALING);
			if (diffExp == 0) {
				diffExp = new Number(0);
			}

		}
	}
	return diffExp;
}
void Mul::oper_sort(){ 
	Sign s = plus;	
	for(iterator i = args_.begin(); i != args_.end(); ++i) {
		if (s == (*i)->sign()) {
			s = plus;
		}
		else {
			s = minus;
		}
		(*i)->sign(plus);
	}
	sign(s);
}
Number Mul::Multiplicity() const {
	Number multiplicity(sign() ? 1 : -1);
	if (args_.size() != 2 || !(*args_.begin())->isNum()) { // if it is not multiple
		return multiplicity;
	}
	multiplicity.setToMultiplication(multiplicity, *((Number *)*args_.begin()));
	return multiplicity;
}
size_t Mul::rsize(const bool all) const {
	size_t suma = 0;
	const_iterator start = args_.begin();
	if (all) {
		if ((*start)->isNum()){
			++start;
		}
	}
	for (const_iterator i = start; i != args_.end(); ++i) {
		suma += (*i)->rsize(false);
	}
	return suma;
}
size_t Mul::asize() const {
	return (*args_.begin())->isNum() ? args_.size()-1 : args_.size();
}
bool Mul::isMultiple() const {
	if (args_.size() == 2) {
		return (*args_.begin())->isNum();
	}
	return false;
}
}
